#include "../lib/useHead.h"
#include "../lib/stringtable.h"
#include "../lib/functable.h"
#include "../lib/instrStream.h"
#include "../lib/scriptHead.h"
#include "../lib/labelTable.h"
#include "../lib/symTable.h"
#include "../lib/script.h"
#include "InstrCode.h"
#include "vm.h"

#define DEST 0
#define SCRE 1

static bool not_implemetion(RunScript &st, Instr &instr, char *error)
{
    sprintf(error, "Exp: not implemetion!");
    return false;
}

static bool instr_mov(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    Value &Scr =  st.resolveOpValue(SCRE);

    if (&Scr == &Dest)
        return true;

    Dest = Scr;
    return true;
}


static bool instr_add(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT)
        Dest._vint += st._Int(SCRE);
    else if (Dest._type == OP_FLAG_TYPE_FLOAT)
        Dest._vfloat += st._Float(SCRE);
    else if (Dest._type == OP_FLAG_TYPE_STRING) {
        Dest._svalue += st._String(SCRE);
    } else{
        sprintf(error, "Add: Unkonw Dest Type %d", Dest._type);
        return false;
    }

    return true;
}


static bool instr_sub(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT)
        Dest._vint -= st._Int(SCRE);
    else if (Dest._type == OP_FLAG_TYPE_FLOAT)
        Dest._vfloat -= st._Float(SCRE);
    else {
        sprintf(error, "Sub: Dest type is not Int or Float!");
        return false;
    }
    
    return true;
}


static bool instr_mul(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT)
        Dest._vint *= st._Int(SCRE);
    else if (Dest._type == OP_FLAG_TYPE_FLOAT)
        Dest._vfloat *= st._Float(SCRE);
    else {
        sprintf(error, "Mul: Dest type is not Int or Float!");
        return false;
    }
    
    return true;
}


static bool instr_div(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) {
        int t = st._Int(SCRE);
        if (t == 0) {
            sprintf(error, "Div: divide by zero!");
            return false;
        }
        Dest._vint /= t;
    } else if (Dest._type == OP_FLAG_TYPE_FLOAT) {
        float t = st._Float(SCRE);
        if (t < 0.000001 && t > -0.000001) {
            sprintf(error, "Div: divide by zero!");
            return false;
        }
        Dest._vfloat /= t;
    } else {
        sprintf(error, "Div: Dest type is not Int or Float!");
        return false;
    }

    return true;
}


static bool instr_mod(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) {
        int t = st._Int(SCRE);
        if (t <= 0) {
            sprintf(error, "Mod: Mod less than zero!");
            return false;
        }
        Dest._vint %= t;
    } else {   
        sprintf(error, "Mod: Dest type is not Int!");
        return false;
    }

    return true;
}


static bool instr_exp(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}

static bool instr_neg(RunScript &st, Instr &instr, char *error) 
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint = - Dest._vint;
    else if (Dest._type == OP_FLAG_TYPE_FLOAT) {
        Dest._vfloat = -Dest._vfloat;
    } else {
        sprintf(error, "Neg: Dest is not Int or Float!");
        return false;
    }
    
    return true;
}

static bool instr_inc(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint += 1;
    else if (Dest._type == OP_FLAG_TYPE_FLOAT) {
        Dest._vfloat += 1;
    } else {
        sprintf(error, "Inc: Dest is not Int or Float!");
        return false;
    }
    
    return true;
}


static bool instr_dec(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint -= 1;
    else if (Dest._type == OP_FLAG_TYPE_FLOAT) {
        Dest._vfloat -= 1;
    } else {
        sprintf(error, "Dec: Dest is not Int or Float!");
        return false;
    }
    
    return true;
}


static bool instr_and(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint &= st._Int(SCRE);
    else {
        sprintf(error, "And: Dest is not Int!");
        return false;
    }
    
    return true;
}

static bool instr_or(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint |= st._Int(SCRE);
    else {
        sprintf(error, "Or: Dest is not Int!");
        return false;
    }
    
    return true;
}


static bool instr_xor(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint = Dest._vint ^ st._Int(SCRE);
    else {
        sprintf(error, "Or: Dest is not Int!");
        return false;
    }
    
    return true;
}


static bool instr_not(RunScript &st, Instr &instr, char *error)
{
    Value &Dest = st.resolveOpValue(DEST);
    
    if (Dest._type == OP_FLAG_TYPE_INT) 
        Dest._vint = !Dest._vint;
    else {
        sprintf(error, "Or: Dest is not Int!");
        return false;
    }
    
    return true;
}


static bool instr_shl(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}

static bool instr_shr(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}

static bool instr_concat(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}


static bool instr_getchar(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}

static bool instr_setchar(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}


static bool instr_jmp(RunScript &st, Instr &instr, char *error)
{
    st.jump( st._Int(DEST));
    return true;
}


static bool instr_je(RunScript &st, Instr &instr, char *error)
{
    Value &op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint == st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat == st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue == st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jne: Unkown souce type!");
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;     
}

static bool instr_jne(RunScript &st, Instr &instr, char *error)
{
    Value &op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint != st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat != st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue != st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jne: Unkown souce type!, %d\n", op1._type);
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;    
}


static bool instr_jg(RunScript &st, Instr &instr, char *error)
{
    Value &op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint > st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat > st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue > st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jg: Unkown souce type!");
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;   
}


static bool instr_jl(RunScript &st, Instr &instr, char *error)
{
    Value &op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint < st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat < st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue < st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jl: Unkown souce type!");
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;     
}


static bool instr_jge(RunScript &st, Instr &instr, char *error)
{
    Value &op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint >= st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat >= st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue >= st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jg: Unkown souce type!");
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;   
}

static bool instr_jle(RunScript &st, Instr &instr, char *error)
{
    Value op1 = st.resolveOpValue(DEST);
    
    bool jmp = false;
    int targetIndex = st._Int(2);
    
    switch(op1._type) {
        case OP_FLAG_TYPE_INT:
            if (op1._vint <= st._Int(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_FLOAT:
            if (op1._vfloat <= st._Float(SCRE))
                jmp = true;
            break;
        case OP_FLAG_TYPE_STRING:
            if (op1._svalue <= st._String(SCRE))
                jmp = true;
            break;
        default:
            sprintf(error, "Jle: Unkown souce type!");
            return false;
    }

    if (jmp)
       st.jump(targetIndex);

    return true;         
}


static bool instr_push(RunScript &st, Instr &instr, char *error)
{
    Value &op = st.resolveOpValue(DEST);
    st.getStack().push(op);
    return true;
}


static bool instr_pop(RunScript &st, Instr &instr, char *error)
{
    Value &op = st.resolveOpValue(DEST);
    op = st.getStack().pop();
    return true;
}


static bool instr_call(RunScript &st, Instr &instr, char *error)
{
    Value &op = st.resolveOpValue(DEST);

    switch(op._type) {
        case OP_FLAG_TYPE_FUNC_INDEX:
            st.step();
            if (!st.callFunc(op._vindex)) {
                sprintf(error, "Call: Func Index out of range!");
                return false;
            }
            break;

        case OP_FLAG_TYPE_HOST_API_CALL: 
            if (!st.callHostFunc(op._vindex)) {
                sprintf(error, "Call: call host api failed!\n");
                return false;
            }
            break;

        default: 
            sprintf(error, "Call: not func type!");
            return false;
    }

    return true;
}


static bool instr_ret(RunScript &st, Instr &instr, char *error)
{
    int funcIndex;
    Value retAddr;
    RunTimeStack  & stack = st.getStack();

    Value &op = stack.getFrameValue();
    
    funcIndex = op._vindex;/*
    if (op._type == OP_FLAG_TYPE_STACK_MARKER)
        g_ExitExecLoop = true;*/

    // pop user pushed data
    stack.popFrame(stack.getUserPushSize());

    // pop local data;
    stack.popFrame(st.funcSize(funcIndex)+1);

    retAddr = stack.getTopValue();
    
    stack.pop(); 
    
    stack.setFrame(op._offset);
    st.jump(retAddr._vindex);   
    return true; 
}


static bool instr_hostcall(RunScript &st, Instr &instr, char *error)
{
    return not_implemetion(st, instr, error);
}


static bool instr_pause(RunScript &st, Instr &instr, char *error)
{
    int duration = st._Int(DEST);
    st.setPause(duration);
}

static bool instr_exit(RunScript &st, Instr &instr, char *error)
{/*
    g_isActive = 0;

    st.s_status.s_isActive &= ~RUNNING;
    for (int i = 0; i < MAX_SCRIPT_NUMBER; ++i) 
        if (XS_IsActive(i)) {
            g_isActive = 1;
            return;
        }*/
    st.stop();
    //exit(0);
}


Execute &getExecute()
{
    Execute *exec = new Execute;

    exec->regist(INSTR_MOV, instr_mov);
    exec->regist(INSTR_ADD,  instr_add);
    exec->regist(INSTR_SUB,  instr_sub);
    exec->regist(INSTR_MUL,  instr_mul);
    exec->regist(INSTR_DIV,  instr_div);
    exec->regist(INSTR_MOD,  instr_mod);
    exec->regist(INSTR_EXP,  instr_exp);
    exec->regist(INSTR_NEG,  instr_neg);
    exec->regist(INSTR_INC,  instr_inc);
    exec->regist(INSTR_DEC,  instr_dec);
    exec->regist(INSTR_AND,  instr_and);
    exec->regist(INSTR_OR,   instr_or);
    exec->regist(INSTR_XOR,  instr_xor);
    exec->regist(INSTR_NOT,  instr_not);
    exec->regist(INSTR_SHL,  instr_shl);
    exec->regist(INSTR_SHR,  instr_shr);
    exec->regist(INSTR_CANCAT,   instr_concat);
    exec->regist(INSTR_GETCHAR,   instr_getchar);
    exec->regist(INSTR_SETCHAR,   instr_setchar);
    exec->regist(INSTR_JMP,   instr_jmp);
    exec->regist(INSTR_JE,   instr_je);
    exec->regist(INSTR_JNE,   instr_jne);
    exec->regist(INSTR_JG,   instr_jg);
    exec->regist(INSTR_JL,   instr_jl);
    exec->regist(INSTR_JGE,   instr_jge);
    exec->regist(INSTR_JLE,   instr_jle);
    exec->regist(INSTR_PUSH,  instr_push);
    exec->regist(INSTR_POP,   instr_pop);
    exec->regist(INSTR_CALL,  instr_call);
    exec->regist(INSTR_RET,  instr_ret);
    exec->regist(INSTR_CALLHOST,  instr_hostcall);
    exec->regist(INSTR_PAUSE,  instr_pause);
    exec->regist(INSTR_EXIT, instr_exit);

    return *exec;
}

#ifdef __MAIN__

int main(int argc, char *argv[])
{
    char filename[128];

    Execute &exec = getExecute();
    RunScript run(exec);

    puts("Put in file Name: ");
    fgets(filename, sizeof(filename), stdin);
    filename[strlen(filename) - 1] = 0;
    run.readFile(filename);
    run.dump();

    run.run();

    system("PAUSE");
    return EXIT_SUCCESS;
}

#endif
